package astar

import (
	"work_test/worklib/priority_queue"
)

type Astar struct {
	// 优先级堆栈
	Queue *priority_queue.PriorityQueue
	// 节点map
	Cells map[int]*Cell
	// 测试
	RunSum int
	// MaxX
	MaxX int
	MaxY int

	ThisP *Cell
	Start int
	End   int

	Path []int
}

type Xy struct {
	X int8
	Y int8
}

var Xy4 = []Xy{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
var Xy8 = []Xy{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {-1, -1}, {-1, 1}, {1, 1}, {1, -1}}

func (aStar *Astar) int2xy(print int) (int8, int8) {
	Y := print/aStar.MaxX + 1
	X := print % aStar.MaxX
	if X == 0 {
		X = aStar.MaxX
		Y--
	}
	return int8(X), int8(Y)
}
func (aStar *Astar) xy2int(x, y int) int {
	return (y-1)*aStar.MaxX + x
}

// 返回最终路径
func (aStar *Astar) getPath(los int) {
	aStar.Path = append(aStar.Path, los)
	thisPath := aStar.ThisP
	for {
		thisLos := aStar.xy2int(int(thisPath.X), int(thisPath.Y))
		aStar.Path = append([]int{thisLos}, aStar.Path...)
		if thisLos == aStar.Start {
			break
		}
		thisPath = thisPath.FatherCell
	}
}

// 队列判断函数
func col(x, y any) bool {
	return (x.(*Cell).H + x.(*Cell).G) > (y.(*Cell).G + y.(*Cell).H)
}

// InitAstar 初始化寻路对象
func InitAstar(maxX, maxY, start, end int) (aStar *Astar) {
	aStar = &Astar{
		Cells: make(map[int]*Cell, 0),
		MaxX:  maxX,
		MaxY:  maxY,
		Queue: priority_queue.NewQueue(col),
		Start: start,
		End:   end,
	}
	return aStar
}

// initObstacle 初始化障碍物品
func (aStar *Astar) initObstacle(obstacleL []int) {
	for _, item := range obstacleL {
		x, y := aStar.int2xy(item)
		obstacleLCell := &Cell{
			X:     x,
			Y:     y,
			State: 3,
		}
		aStar.Cells[item] = obstacleLCell
	}
}

// 开始寻路
func (aStar *Astar) findPath() {
	// 初始点
	x, y := aStar.int2xy(aStar.Start)
	// 制造边界
	aStar.ThisP = &Cell{
		X:          x,
		Y:          y,
		G:          0,
		H:          getH(aStar, x, y),
		FatherCell: aStar.ThisP,
		State:      1,
	}
	aStar.Cells[aStar.Start] = aStar.ThisP
	// 开始寻路
	for {
		aStar.RunSum++
		for _, i := range Xy4 {
			thisX := aStar.ThisP.X + i.X
			thisY := aStar.ThisP.Y + i.Y
			if thisX > 0 && thisX <= int8(aStar.MaxX) && thisY > 0 && thisY <= int8(aStar.MaxY) {
				// 获取当前边界坐标
				los := aStar.xy2int(int(thisX), int(thisY))
				// 若此节点为终点寻路返回
				if los == aStar.End {
					aStar.getPath(los)
					break
				}
				thisCell := aStar.Cells[los]
				// 若检索格子未探索
				if thisCell == nil {
					cell1 := InitCell(aStar, thisX, thisY)
					aStar.Cells[los] = cell1
					aStar.Queue.Push(cell1)
					//aStar.showQueue()
				} else if thisCell.State == 1 {
					if thisCell.G > aStar.ThisP.G+1 {
						thisCell.G = aStar.ThisP.G + 1
						thisCell.FatherCell = aStar.ThisP
						// G 值改变 优先队列是否需要重新排布
					}
				}
			}
		}
		// 设置当前父节点为搜索过的节点
		aStar.ThisP.State++
		// 结束判定
		if aStar.Queue.Empty() || aStar.Path != nil {
			break
		}

		// 通过优先链表获取GH最小节点并定义成新的父节点
		aStar.ThisP = aStar.Queue.Pop().(*Cell)

	}

}
